﻿using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using Microsoft.WindowsAzure.Storage.Table.DataServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BackupManager.Storage
{
    public static class TableStorageHelper
    {
        public static DateTime NullableDateTimeToDateTime(DateTime? dateTime)
        {
            if (dateTime.HasValue)
                return dateTime.Value;
            return new DateTime(1900, 1, 1);
        }

        public static DateTime? DateTimeToNullableDateTime(DateTime dateTime)
        {
            if (dateTime.Equals(new DateTime(1900, 1, 1)))
                return null;
            return dateTime;
        }

        public static DateTime NullableDateTimeOffsetToDateTime(DateTimeOffset? dateTimeOffset)
        {
            if (dateTimeOffset.HasValue)
                return dateTimeOffset.Value.UtcDateTime;
            return new DateTime(1900, 1, 1);
        }

        public static DateTimeOffset? DateTimeToNullableDateTimeOffset(DateTime dateTime)
        {
            if (dateTime.Equals(new DateTime(1900, 1, 1)))
                return null;
            return new DateTimeOffset(dateTime);
        }
    }

    public abstract class TableContextBase<T> : TableServiceContext
        where T:TableServiceEntity
    {
        public TableContextBase(CloudTableClient cloudTableClient)
            : base(cloudTableClient)
        {
            var cloudTable = cloudTableClient.GetTableReference(GetTableName());
            cloudTable.CreateIfNotExists();
        }

        public void Upsert(T entity)
        {
            TableServiceEntity existingEntity = null;
            try
            {
                existingEntity = (from e in CreateQuery()
                                  where e.PartitionKey.Equals(entity.PartitionKey) &&
                                  e.RowKey.Equals(entity.RowKey)
                                  select e).FirstOrDefault();
            }
            catch { }

            if (existingEntity == null)
            {
                AddObject(GetTableName(), entity);
            }
            else
            {
                foreach (var propertyGet in typeof(T).GetProperties().Where((p)=>p.CanRead))
                {
                    var propertySet = existingEntity.GetType().GetProperty(propertyGet.Name);
                    if (propertySet != null && propertySet.CanWrite)
                    {
                        var value = propertyGet.GetValue(entity);
                        if (propertyGet.GetCustomAttributes(typeof(DoNotUpdateIfIsNullAttribute), true).Length == 0 || value != null)
                        {
                            propertySet.SetValue(existingEntity, value);
                        }
                    }
                }
                UpdateObject(existingEntity);
            }
            SaveChangesWithRetries();
        }

        public void Delete(string partitionKey, string rowKey)
        {
            TableServiceEntity existingEntity = GetEntity(partitionKey, rowKey);

            if (existingEntity != null)
            {
                DeleteObject(existingEntity);
                SaveChangesWithRetries();
            }
        }

        public T GetEntity(string partitionKey, string rowKey)
        {
            try
            {
                return (from e in CreateQuery()
                                  where e.PartitionKey.Equals(partitionKey) &&
                                  e.RowKey.Equals(rowKey)
                                  select e).FirstOrDefault();
            }
            catch { }
            return null;
        }

        public IQueryable<T> CreateQuery()
        {
            return CreateQuery<T>(GetTableName());
        }

        public abstract string GetTableName();
    }
}
